use crate::cjson::cjson_print_from_value;
use crate::constants::{HUAWEI_IT_PRODUCT_CA, RSA_PUBLIC_EXPONENT};
use crate::evidence::{AkCert, AkCertNoAs, ITrusteeEvidence, ReportSign};
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use base64::Engine;
use openssl::{
    bn::BigNum,
    pkey::{PKey, Public},
    rsa::Rsa,
    x509::X509,
};
use plugin_manager::PluginError;
use serde_json::Value;

/// NO AS scenario
impl ITrusteeEvidence {
    pub(crate) fn verify_no_as(&self) -> Result<Value, PluginError> {
        let AkCert::SceNoAs { sce_no_as: ak_cert_no_as } = &self.ak_cert;

        let ReportSign::SceNoAs { sce_no_as: report_sign_no_as } = &self.report_sign;

        // --- Step 1: Verify DRK certificate ---
        let drk_cert = Self::verify_drk_cert(ak_cert_no_as)?;

        // --- Step 2: Verify DRK signature on AK certificate payload ---
        let ak_payload = Self::get_ak_payload_json(&self.json_value)?;
        Self::verify_payload_of_ak_cert(ak_payload, ak_cert_no_as, drk_cert)?;

        // --- Step 3: Extract AK public key ---
        let ak_pubkey = Self::extract_ak_pub_key(&ak_cert_no_as)?;

        // --- Step 4: Verify AK signature on final report payload ---
        self.verify_payload(&report_sign_no_as, ak_pubkey)?;

        // --- All verifications successful ---
        Ok(self.json_value.clone())
    }

    fn verify_drk_cert(ak_cert_no_as: &AkCertNoAs) -> Result<X509, PluginError> {
        // Load root CA certificate
        let ca_cert_pem = HUAWEI_IT_PRODUCT_CA.as_bytes();
        let ca_cert = X509::from_pem(&ca_cert_pem)
            .map_err(|e| PluginError::InternalError(format!("load root CA cert failed: {e}")))?;
        Self::verify_cert_validity(&ca_cert, "Huawei_IT_Product_CA")?;

        // Decode and parse DRK certificate
        let drk_cert_pem = Self::restore_pem_cert(&ak_cert_no_as.signature.drk_cert)?;
        let cert = X509::from_pem(drk_cert_pem.as_bytes())
            .map_err(|e| PluginError::InternalError(format!("load cert failed: {e}")))?;
        Self::verify_cert_validity(&ca_cert, "DRK")?;

        // Use root certificate to verify DRK certificate
        Self::verify_cert_by_cert(&ca_cert, &cert)?;
        Ok(cert)
    }

    /// Wrap Base64 encoded raw certificate data into PEM format
    fn restore_pem_cert(b64_str: &str) -> Result<String, PluginError> {
        // 1. base64 decode
        let decoded = URL_SAFE_NO_PAD
            .decode(b64_str)
            .map_err(|e| PluginError::InternalError(format!("decode pem failed: {e}")))?;

        // 2. Convert to string (originally base64 string)
        let mut base64_text = String::from_utf8(decoded).map_err(|e| PluginError::InternalError(format!("{e}")))?;

        // 3. Remove possible trailing '\0'
        while base64_text.ends_with('\0') {
            base64_text.pop();
        }

        // 4. Insert PEM header and footer
        let head = "-----BEGIN CERTIFICATE-----\n";
        let end = "-----END CERTIFICATE-----\n";

        // Line break every 64 characters
        let mut body = String::new();
        for chunk in base64_text.as_bytes().chunks(64) {
            body.push_str(&String::from_utf8_lossy(chunk));
            body.push('\n');
        }

        // 5. Concatenate complete PEM
        let pem = format!("{}{}{}", head, body, end);

        Ok(pem)
    }

    /// Verify AK certificate payload signature based on DRK certificate
    fn verify_payload_of_ak_cert(
        ak_payload: &Value,
        ak_cert_no_as: &AkCertNoAs,
        drk_cert: X509,
    ) -> Result<(), PluginError> {
        let drk_pubkey =
            drk_cert.public_key().map_err(|e| PluginError::InternalError(format!("get DRK pubkey failed: {e}")))?;

        let payload = cjson_print_from_value(ak_payload)?;
        let encode_payload = URL_SAFE_NO_PAD.encode(payload);

        Self::verify_sign_by_key(&encode_payload, &ak_cert_no_as.signature.drk_sign, drk_pubkey)
    }

    fn extract_ak_pub_key(ak_cert_no_as: &AkCertNoAs) -> Result<PKey<Public>, PluginError> {
        let ak_pub = &ak_cert_no_as.payload.ak_pub;
        let ak_pub_json = match ak_pub.key_type.as_str() {
            "RSA" => ak_pub
                .extra
                .get("n")
                .ok_or_else(|| PluginError::InputError("missing field 'n' of RSA pub key".to_string()))?,
            "DAA" => ak_pub
                .extra
                .get("qs")
                .ok_or_else(|| PluginError::InputError("missing field 'qs' of DAA pub key".to_string()))?,
            _ => return Err(PluginError::InternalError("key type error".to_string())),
        };

        // Base64URL decode AK public key modulus (n)
        let n_b64 = ak_pub_json.as_str().ok_or_else(|| PluginError::InputError("missing RSA.n".into()))?;
        let n_bytes = URL_SAFE_NO_PAD
            .decode(n_b64.as_bytes())
            .map_err(|e| PluginError::InternalError(format!("decode n failed: {e}")))?;
        let n = BigNum::from_slice(&n_bytes)
            .map_err(|e| PluginError::InternalError(format!("decode mod number of AK pub key failed: {e}")))?;

        // Use standard public key exponent e = 65537 (RSA_PUBLIC_EXPONENT)
        let e = BigNum::from_u32(RSA_PUBLIC_EXPONENT)
            .map_err(|e| PluginError::InternalError(format!("Failed to create RSA exponent: {}", e)))?;

        // Build RSA public key
        let rsa = Rsa::from_public_components(n, e)
            .map_err(|e| PluginError::InternalError(format!("build RSA pub key failed: {e}")))?;
        let ak_pubkey: PKey<Public> =
            PKey::from_rsa(rsa).map_err(|e| PluginError::InternalError(format!("build pub key failed: {e}")))?;
        Ok(ak_pubkey)
    }
}
